#region Copyright Notice

// 
// 
//  
//  Copyright (c) 2009-2011 Anindya Chatterjee
//  
//  Licensed under the Apache License, Version 2.0 (the "License");
//  you may not use this file except in compliance with the License.
//  You may obtain a copy of the License at
//  
//      http://www.apache.org/licenses/LICENSE-2.0
//  
//  Unless required by applicable law or agreed to in writing, software
//  distributed under the License is distributed on an "AS IS" BASIS,
//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//  See the License for the specific language governing permissions and
//  limitations under the License.
// 
// 

#endregion

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Permissions;
using AbstractClass.Extensions;
using Shell32;

namespace AbstractClass.IO
{
    /// <summary>
    /// Provides a set of static methods to query and manipulate the Recycle Bin.
    /// </summary>
    public static class RecycleBin
    {
        /// <summary>
        /// Queries the Recycle Bin to get a list of all deleted items.
        /// </summary>
        /// <returns>
        /// A list of <see cref="RecycledItem"/> retrieved from Recycle Bin.
        /// </returns>
        [SecurityPermission(SecurityAction.LinkDemand)]
        public static List<RecycledItem> QueryRecycleBinItems()
        {
            var shell = new Shell();
            Folder recycleBin = shell.NameSpace(10);
            List<RecycledItem> items = (from FolderItem folderItem in recycleBin.Items()
                                        select new RecycledItem
                                                   {
                                                       Name = recycleBin.GetDetailsOf(folderItem, 0),
                                                       Origin =
                                                           new DirectoryInfo(recycleBin.GetDetailsOf(folderItem, 1)),
                                                       Deleted = ParseDate(recycleBin.GetDetailsOf(folderItem, 2)),
                                                       Size = recycleBin.GetDetailsOf(folderItem, 3),
                                                       ItemType = recycleBin.GetDetailsOf(folderItem, 4),
                                                       Modified = ParseDate(recycleBin.GetDetailsOf(folderItem, 5)),
                                                       Attribute = recycleBin.GetDetailsOf(folderItem, 8),
                                                       FolderItem = folderItem
                                                   }).ToList();

            Marshal.FinalReleaseComObject(shell);
            return items;
        }

        /// <summary>
        /// Restores all deleted items from the Recycle Bin.
        /// </summary>
        [SecurityPermission(SecurityAction.LinkDemand)]
        public static void RestoreAll()
        {
            List<RecycledItem> items = QueryRecycleBinItems();
            foreach (RecycledItem recycledItem in items)
            {
                recycledItem.Restore();
            }
        }

        /// <summary>
        /// Restores a specific deleted item from the Recycle Bin.
        /// </summary>
        /// <param name="itemName">Name of the item to restore.</param>
        /// <returns>
        /// Returns <c>true</c> if restore operation successful; otherwise, <c>false</c>.
        /// </returns>
        [SecurityPermission(SecurityAction.LinkDemand)]
        public static bool Restore(string itemName)
        {
            List<RecycledItem> items = QueryRecycleBinItems();
            foreach (RecycledItem recycledItem in
                items.Where(
                    recycledItem => recycledItem.Name.Equals(itemName, StringComparison.OrdinalIgnoreCase)))
            {
                recycledItem.Restore();
                return true;
            }
            return false;
        }

        /// <summary>
        /// Empties the Recycle Bin by deleting all items from it.
        /// </summary>
        /// <returns>
        /// Returns <c>true</c> if empty operation is successful; otherwise, <c>false</c>.
        /// </returns>
        [SecurityPermission(SecurityAction.LinkDemand)]
        public static bool EmptyRecycleBin()
        {
            uint result = NativeMethods.SHEmptyRecycleBin(IntPtr.Zero, "",
                                                          RecycleFlags.NoSound | RecycleFlags.NoConfirmation);

            if (result == 0)
                return true;
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        /// <summary>
        /// Gets the Recycle Bin details.
        /// </summary>
        /// <returns>A <see cref="RecycleBinInfo"/> item containing the information about the Recycle Bin.</returns>
        [SecurityPermission(SecurityAction.LinkDemand)]
        public static RecycleBinInfo GetRecycleBinInfo()
        {
            var info = new RecycleBinInfo();
            var query = new SHQUERYRBINFO {cbSize = Marshal.SizeOf(typeof (SHQUERYRBINFO))};
            int result = NativeMethods.SHQueryRecycleBin(null, ref query);

            if (result != 0)
                throw new Win32Exception(Marshal.GetLastWin32Error());

            info.NumberOfItems = query.i64NumItems.ToString();
            info.Size = string.Format("{0}",
                                      (Convert.ToDouble(query.i64Size) / Convert.ToDouble(1024) / Convert.ToDouble(1024))
                                          .
                                          ToString("#,###.##"));
            info.RecycledItems = QueryRecycleBinItems();
            return info;
        }

        private static DateTime ParseDate(string dateString)
        {
            // file date contains some invalid Unicode character.
            dateString = dateString.ToAscii();
            string[] formats = {
                                   "M/d/yyyy h:mm:ss tt", "M/d/yyyy h:mm tt",
                                   "MM/dd/yyyy hh:mm:ss", "M/d/yyyy h:mm:ss",
                                   "M/d/yyyy hh:mm tt", "M/d/yyyy hh tt",
                                   "M/d/yyyy h:mm", "M/d/yyyy h:mm",
                                   "MM/dd/yyyy hh:mm", "M/dd/yyyy hh:mm"
                               };

            try
            {
                return DateTime.ParseExact(dateString, formats,
                                           CultureInfo.InvariantCulture,
                                           DateTimeStyles.None);
            }
            catch (FormatException)
            {
                return default(DateTime);
            }
        }
    }
}